﻿using System;
using NUnit.Framework;
using RenewalProjects.Libraries.Common.Testing.Attributes;
using RenewalProjects.Libraries.Wcf.EasyProxy.Pools;
using RenewalProjects.Libraries.Wcf.EasyProxy.Tests.FixtureHelpers;
using RenewalProjects.Libraries.Wcf.EasyProxy.Tests.TestData;

namespace RenewalProjects.Libraries.Wcf.EasyProxy.Tests.Fixtures
{
	[TestFixture]
	public class ResourceListPoolFixture
	{
		#region Test Methods

		[ExpectedException(typeof(ArgumentOutOfRangeException))]
		[Test]
		public void ResourceListPool_constructor_fail_max_items([Values(-2, -1, 0)] int maxItemsNumber)
		{
			new ResourceListPool<TestItem>(maxItemsNumber);
		}

		[ExpectedException(typeof(InvalidOperationException))]
		[Test]
		public void ResourceListPool_fail_min_refill_threshold([Values(-2.0f, -1.0f, 0.0f)] float threshold)
		{
			new ResourceListPool<TestItem>()
			    .ResourceRefillThreshold = threshold;
		}

		[ExpectedException(typeof(InvalidOperationException))]
		[Test]
		public void ResourceListPool_fail_max_refill_threshold([Values(100.001f, 101.0f, 102.0f)] float threshold)
		{
			new ResourceListPool<TestItem>()
			    .ResourceRefillThreshold = threshold;
		}

		[ExpectedException(typeof(InvalidOperationException))]
		[Test]
		public void ResourceListPool_fail_when_setting_factory_to_null()
		{
			new ResourceListPool<TestItem>()
			    .ResourceItemFactory = null;
		}

		[ExpectedException(typeof(InvalidOperationException))]
		[Test]
		public void ResourceListPool_fail_when_replenishing_resources_with_no_factory()
		{
			new ResourceListPool<TestItem>(1)
			    .RefreshResources();
		}

		[Test]
		public void ResourceListPool_initial_replenishment_of_1_item()
		{
			new ResourceListPoolFixtureHelper(1)
			    .AddItems(1)
			    .RefreshResources()
			    .CheckFactoryCallCount(1);
		}

		[Test]
		public void ResourceListPool_replenish_expired_items()
		{
			new ResourceListPoolFixtureHelper(2)
			    .AddItems(3)
			    .RefreshResources()
			    .CheckFactoryCallCount(2)
			    .ExpireItem(0)
			    .RefreshResources()
			    .AssertItems(new[] {0}, (i)=>i.DrainCalls > 0)
			    .AssertItems(new[] {1, 2}, (i)=>i.DrainCalls == 0 && !i.InUse);
		}

		[Test]
		public void ResourceListPool_get_available_item_with_max_items_2()
		{
			new ResourceListPoolFixtureHelper(2)
			    .AddItems(2)
			    .ClientsRequestAvailableItems(1)
			    .VerifyCurrentItemWithPrecreatedItem(0)
			    .Done()
			    .AssertItems(new[] {0}, (i)=>i.InUse);
		}

		[Test]
		public void ResourceListPool_get_available_item_after_one_expired_with_max_items_2()
		{
			new ResourceListPoolFixtureHelper(2)
			    .AddItems(2)
			    .RefreshResources()
			    .ExpireItem(0)
			    .ClientsRequestAvailableItems(1)
			    .VerifyCurrentItemWithPrecreatedItem(1).Done()
			    .AssertItems(new[] {1}, (i)=>i.DrainCalls == 0 && i.InUse);
		}

		[Test, Description("Make sure it does only the one-pass for this version")]
		public void ResourceListPool_get_null_when_only_available_item_is_in_use()
		{
			new ResourceListPoolFixtureHelper(1)
			    .AddItems(1)
			    .RefreshResources()
			    .OccupyItem(0, true)
			    .ClientsRequestAvailableItems(1)
			    .CurrentItemIsNull();
		}

		[MultithreadTest]
		[Test]
		public void ResourceListPool_2_threads_3_resources()
		{
			new ResourceListPoolFixtureHelper(3)
			    .AddItems(3)
			    .RefreshResources()
			    .ClientsRequestAvailableItems(2)
			    .Done()
			    .AssertItems(new[] {0, 1}, (i)=>i.DrainCalls == 0 && i.InUse)
			    .AssertItems(new[] {2}, (i)=>i.DrainCalls == 0 && !i.InUse);
		}

		[MultithreadTest]
		[Test]
		public void ResourceListPool_3_threads_2_resources()
		{
			new ResourceListPoolFixtureHelper(2)
			    .AddItems(2)
			    .RefreshResources()
			    .ClientsRequestAvailableItems(3)
			    .CheckAllReturnedItemsFor(1, (i)=>i == null)
			    .CheckAllReturnedItemsFor(2, (i)=>i != null && i.InUse);
		}

		[MultithreadTest]
		[Test]
		public void ResourceListPool_2_threads_max_2_resources_called_consecutively_with_1_resource_in_use()
		{
			new ResourceListPoolFixtureHelper(2)
			    .AddItems(3)
			    .RefreshResources()
			    .ClientsRequestAvailableItems(2)
			    .CheckAllReturnedItemsFor(2, (i)=>i.InUse)
			    .Done()
			    .OccupyItem(0, false)
			    .ClientsRequestAvailableItems(2)
			    .CheckAllReturnedItemsFor(1, (i)=>i != null && i.InUse)
			    .CheckAllReturnedItemsFor(1, (i)=>i == null);
		}

		[MultithreadTest]
		[Test]
		public void ResourceListPool_3_threads_2_resources_no_refresh_in_beginning()
		{
			new ResourceListPoolFixtureHelper(2)
			    .AddItems(2)
			    .ClientsRequestAvailableItems(3)
			    .CheckAllReturnedItemsFor(1, (i)=>i == null)
			    .CheckAllReturnedItemsFor(2, (i)=>i != null && i.InUse);
		}

		#endregion
	}
}